package org.eiahe.hr.attendance.client;

import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eiahe.hr.attendance.config.DingTalkProperties;
import org.eiahe.hr.attendance.domain.DingTalkAttendanceRecord;
import org.eiahe.hr.attendance.domain.DingTalkLeaveRecord;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 钉钉API客户端
 *
 * @author zhangnn
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DingTalkApiClient {

    private final DingTalkProperties dingTalkProperties;

    /**
     * 钉钉API基础URL
     */
    private static final String BASE_URL = "https://oapi.dingtalk.com";

    /**
     * 获取AccessToken的URL
     */
    private static final String GET_TOKEN_URL = BASE_URL + "/gettoken";

    /**
     * 获取考勤记录的URL
     */
    private static final String GET_ATTENDANCE_URL = BASE_URL + "/attendance/listRecord";
    /**
     * 获取请假信息URL
     */
    private static final String GET_LEAVE_URL = BASE_URL + "/attendance/getleavestatus";

    /**
     * AccessToken缓存
     */
    private String accessToken;
    private LocalDateTime tokenExpireTime;

    /**
     * 获取AccessToken
     */
    public String getAccessToken() {
        // 如果token还有效,直接返回
        if (accessToken != null && tokenExpireTime != null
                && LocalDateTime.now().isBefore(tokenExpireTime)) {
            return accessToken;
        }

        try {
            String url = GET_TOKEN_URL + "?appkey=" + dingTalkProperties.getAppKey()
                    + "&appsecret=" + dingTalkProperties.getAppSecret();

            HttpResponse response = HttpRequest.get(url).execute();
            String body = response.body();

            JSONObject json = JSONUtil.parseObj(body);
            if (json.getInt("errcode") == 0) {
                accessToken = json.getStr("access_token");
                // token有效期2小时,提前10分钟刷新
                tokenExpireTime = LocalDateTime.now().plusMinutes(110);
                log.info("获取钉钉AccessToken成功");
                return accessToken;
            } else {
                log.error("获取钉钉AccessToken失败: {}", body);
                throw new RuntimeException("获取钉钉AccessToken失败: " + json.getStr("errmsg"));
            }
        } catch (Exception e) {
            log.error("获取钉钉AccessToken异常", e);
            throw new RuntimeException("获取钉钉AccessToken异常", e);
        }
    }

    /**
     * 获取考勤记录
     *
     * @param userIds   用户ID列表
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 考勤记录列表
     */
    public List<DingTalkAttendanceRecord> getAttendanceRecords(List<String> userIds,
            LocalDate startDate, LocalDate endDate) {

        String token = getAccessToken();
        List<DingTalkAttendanceRecord> allRecords = new ArrayList<>();

        try {
            // 转换日期格式
            Date start = Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
            Date end = Date.from(endDate.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());

            // 构建请求参数
            JSONObject params = new JSONObject();
            params.set("userIdList", userIds);
            params.set("workDateFrom", DateUtil.format(start, "yyyy-MM-dd HH:mm:ss"));
            params.set("workDateTo", DateUtil.format(end, "yyyy-MM-dd HH:mm:ss"));
            params.set("offset", 0);
            params.set("limit", dingTalkProperties.getPageSize());

            String url = GET_ATTENDANCE_URL + "?access_token=" + token;

            HttpResponse response = HttpRequest.post(url)
                    .body(params.toString())
                    .execute();

            String body = response.body();
            JSONObject json = JSONUtil.parseObj(body);

            if (json.getInt("errcode") == 0) {
                JSONObject result = json.getJSONObject("result");
                if (result != null) {
                    JSONArray recordList = result.getJSONArray("recordresult");
                    if (recordList != null) {
                        for (int i = 0; i < recordList.size(); i++) {
                            JSONObject record = recordList.getJSONObject(i);
                            DingTalkAttendanceRecord attendanceRecord = parseAttendanceRecord(record);
                            if (attendanceRecord != null) {
                                allRecords.add(attendanceRecord);
                            }
                        }
                    }
                }
                log.info("获取钉钉考勤记录成功,共{}条", allRecords.size());
            } else {
                log.error("获取钉钉考勤记录失败: {}", body);
                throw new RuntimeException("获取钉钉考勤记录失败: " + json.getStr("errmsg"));
            }
        } catch (Exception e) {
            log.error("获取钉钉考勤记录异常", e);
            throw new RuntimeException("获取钉钉考勤记录异常", e);
        }

        return allRecords;
    }

    /**
     * 解析考勤记录
     */
    private DingTalkAttendanceRecord parseAttendanceRecord(JSONObject record) {
        try {
            DingTalkAttendanceRecord attendanceRecord = new DingTalkAttendanceRecord();
            attendanceRecord.setUserId(record.getStr("userId"));
            attendanceRecord.setUserCheckTime(record.getDate("userCheckTime"));
            attendanceRecord.setCheckType(record.getStr("checkType"));
            attendanceRecord.setLocationResult(record.getStr("locationResult"));
            attendanceRecord.setLocationMethod(record.getStr("locationMethod"));
            attendanceRecord.setDeviceId(record.getStr("deviceId"));
            attendanceRecord.setBaseCheckTime(record.getDate("baseCheckTime"));
            /**
             * Normal：正常、Early：早退、Late：迟到、SeriousLate：严重迟到、Absenteeism：旷工迟到、NotSigned：未打卡
             */
            attendanceRecord.setTimeResult(record.getStr("timeResult"));
            attendanceRecord.setLocationTitle(record.getStr("locationTitle"));
            attendanceRecord.setSourceType(record.getStr("sourceType"));

            return attendanceRecord;
        } catch (Exception e) {
            log.error("解析考勤记录失败", e);
            return null;
        }
    }

    /**
     * 根据手机号获取钉钉用户ID
     */
    public String getUserIdByMobile(String mobile) {
        String token = getAccessToken();

        try {
            String url = BASE_URL + "/topapi/v2/user/getbymobile?access_token=" + token;
            JSONObject params = new JSONObject();
            params.set("mobile", mobile);

            HttpResponse response = HttpRequest.post(url)
                    .body(params.toString())
                    .execute();

            String body = response.body();
            JSONObject json = JSONUtil.parseObj(body);

            if (json.getInt("errcode") == 0) {
                return json.getStr("userid");
            } else {
                log.warn("根据手机号{}获取钉钉用户ID失败: {}", mobile, json.getStr("errmsg"));
                return null;
            }
        } catch (Exception e) {
            log.error("根据手机号获取钉钉用户ID异常", e);
            return null;
        }
    }

    /**
     * 获取请假记录
     *
     * @param userIds   用户ID列表
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 请假记录列表
     */
    public List<DingTalkLeaveRecord> getLeaveRecords(List<String> userIds,
            LocalDate startDate, LocalDate endDate) {

        String token = getAccessToken();
        List<DingTalkLeaveRecord> allRecords = new ArrayList<>();

        try {
            // 转换日期格式
            long startTime = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
            long endTime = endDate.atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

            for (String userId : userIds) {
                // 构建请求参数
                JSONObject params = new JSONObject();
                params.set("userid_list", List.of(userId));
                params.set("start_time", startTime);
                params.set("end_time", endTime);

                String url = GET_LEAVE_URL + "?access_token=" + token;

                HttpResponse response = HttpRequest.post(url)
                        .body(params.toString())
                        .execute();

                String body = response.body();
                JSONObject json = JSONUtil.parseObj(body);

                if (json.getInt("errcode") == 0) {
                    JSONObject result = json.getJSONObject("result");
                    if (result != null) {
                        JSONArray leaveList = result.getJSONArray("leave_status");
                        if (leaveList != null) {
                            for (int i = 0; i < leaveList.size(); i++) {
                                JSONObject record = leaveList.getJSONObject(i);
                                DingTalkLeaveRecord leaveRecord = parseLeaveRecord(record);
                                if (leaveRecord != null) {
                                    allRecords.add(leaveRecord);
                                }
                            }
                        }
                    }
                } else {
                    log.warn("获取用户{}请假记录失败: {}", userId, json.getStr("errmsg"));
                }
            }

            log.info("获取钉钉请假记录成功,共{}条", allRecords.size());
        } catch (Exception e) {
            log.error("获取钉钉请假记录异常", e);
            throw new RuntimeException("获取钉钉请假记录异常", e);
        }

        return allRecords;
    }

    /**
     * 解析请假记录
     */
    private DingTalkLeaveRecord parseLeaveRecord(JSONObject record) {
        try {
            DingTalkLeaveRecord leaveRecord = new DingTalkLeaveRecord();

            leaveRecord.setUserId(record.getStr("userid"));
            leaveRecord.setLeaveCode(record.getStr("leave_code"));
            leaveRecord.setStartTime(new Date(record.getLong("start_time")));
            leaveRecord.setEndTime(new Date(record.getLong("end_time")));
            leaveRecord.setDurationUnit(record.getStr("duration_unit"));
            leaveRecord.setDurationPercent(record.getStr("duration_percent"));
            leaveRecord.setApproveState(record.getStr("approve_state"));
            leaveRecord.setTagNames(record.getStr("tagnames"));
            leaveRecord.setBizId(record.getStr("biz_id"));

            return leaveRecord;
        } catch (Exception e) {
            log.error("解析请假记录失败", e);
            return null;
        }
    }

}
